﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace RSA加密与解密
{
    public class RSATool
    {
        /// <summary>
        /// 密钥
        /// 使用的是：D:\Files\网络继电器组态软件\RSA公钥与密钥   中的prikey.pem
        /// 1、先输入""
        /// 2、把Pem文件里的内容复制过来
        /// 3、在前面加上@符号
        /// </summary>
        string priKeyPem = @"-----BEGIN RSA PRIVATE KEY-----
                            MIICXAIBAAKBgQD6PjEDGPwHT5hpG2hoAi1AI94RsR566wNT88tJSpVXOWh4/YZP
                            Rn/5M5TojSD6wH5IwvS4CQ27QGkeK53BJkR4xMKRQ+btkslPCl3EXZiajzhynumX
                            vPM+MMXg6Z/yADNz5nxMIs66k+1HyS8qPjId1d8PMQtKvhLs6svYeQf+PwIDAQAB
                            AoGAaAT2Bp2aFcfmSLqV2bYd7S17n8sKKnoKNhl/BQY1sb4xSSnJbKaXh5UDbu9L
                            KDBfzgsMAnvw80dA6sOinak0iFSFOeqYkmO3DDVW+7ulCfMaxHrZe8COHc7AOP/N
                            UUMW5r2pkep+LKhJsbfVDQ6bBCQM3/RySFUfBX4pUzbrFVECQQD+S98jTo/mK9/8
                            kK9udmoQvEeOORKSAgrbU3NA314vNrAl6Cza5UZPxXglreYG8DbEuYMj+7kxS2EX
                            vRVGyvblAkEA++teNiXcSkmSzCQD3OB4Bk5jgjo9nd8xJT0hJYHZolZ9wZcwjW6V
                            ale33jsF5YApa50nwEhzxCd3tAUu4vEKUwJANPb0iNQKILSuuFoiT+2V9dphRPwa
                            JBMM09WzwaVEZq0LTznmbcknNeI5Pg/UQ0mw/pRs/gel8OB0Xo9haRdJCQJAL1l3
                            5bmMBDJ78v658yQFqznEnk+QEUJIHHW26zPS0h5rmJ1wNMUaNFi0aIKzgwVpw55Q
                            QGXdzHqWlzF6NJGVnQJBAM6vgdM2Q9OwVhXvf0g0dGukD/DGqqF8e1pZGTosBY5z
                            4dRQamzJVel8L3KYaZwAPLNP6Y4jS1NQdVdtov616qw=
                            -----END RSA PRIVATE KEY-----";
        public string PriKey { get => this.priKeyPem; }


        public string Encrypt(string strText, string strPublicKey)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(strPublicKey);
            byte[] byteText = Encoding.UTF8.GetBytes(strText);
            byte[] byteEntry = rsa.Encrypt(byteText, false);
            return Convert.ToBase64String(byteEntry);
        }

        public string Decrypt(string strEntryText, string strPrivateKey)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(strPrivateKey);
            byte[] byteEntry = Convert.FromBase64String(strEntryText);
            byte[] byteText = rsa.Decrypt(byteEntry, false);
            return Encoding.UTF8.GetString(byteText);
        }

        public Dictionary<string, string> GetKey()
        {
            Dictionary<string, string> dictKey = new Dictionary<string, string>();
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            dictKey.Add("PublicKey", rsa.ToXmlString(false));
            dictKey.Add("PrivateKey", rsa.ToXmlString(true));
            return dictKey;
        }

        /// <summary>
        /// 获取私钥
        /// </summary>
        /// <param name="priKeyPem"></param>
        /// <returns></returns>
        public string GetPriKey(string priKeyPem)
        {
            string priKey = priKeyPem;

            priKey = priKey.Replace("-----BEGIN RSA PRIVATE KEY-----", "");
            priKey = priKey.Replace("-----END RSA PRIVATE KEY-----", "");

            RSACryptoServiceProvider rsaProvider = DecodeRSAPrivateKey(priKey);
            string PrivateKey = rsaProvider.ToXmlString(true);//将RSA算法的私钥导出到字符串PrivateKey中，参数为true表示导出私钥
            
            return PrivateKey;
        }
        /// <summary>
        /// 获取公钥
        /// </summary>
        /// <param name="priKeyPem"></param>
        /// <returns></returns>
        public string GetPublicKey(string priKeyPem)
        {
            string priKey = priKeyPem;

            priKey = priKey.Replace("-----BEGIN RSA PRIVATE KEY-----", "");
            priKey = priKey.Replace("-----END RSA PRIVATE KEY-----", "");

            RSACryptoServiceProvider rsaProvider = DecodeRSAPrivateKey(priKey);
            string PrivateKey = rsaProvider.ToXmlString(false);//将RSA算法的私钥导出到字符串PrivateKey中，参数为true表示导出私钥

            return PrivateKey;
        }

        public RSACryptoServiceProvider DecodeRSAPrivateKey(string priKey)
        {
            var privkey = Convert.FromBase64String(priKey);
            byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
            // ---------  Set up stream to decode the asn.1 encoded RSA private key  ------
            MemoryStream mem = new MemoryStream(privkey);
            BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading
            byte bt = 0;
            ushort twobytes = 0;
            int elems = 0;
            try
            {
                twobytes = binr.ReadUInt16();
                if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
                    binr.ReadByte();        //advance 1 byte
                else if (twobytes == 0x8230)
                    binr.ReadInt16();       //advance 2 bytes
                else
                    return null;
                twobytes = binr.ReadUInt16();
                if (twobytes != 0x0102) //version number
                    return null;
                bt = binr.ReadByte();
                if (bt != 0x00)
                    return null;
                //------  all private key components are Integer sequences ----
                elems = GetIntegerSize(binr);
                MODULUS = binr.ReadBytes(elems);
                elems = GetIntegerSize(binr);
                E = binr.ReadBytes(elems);
                elems = GetIntegerSize(binr);
                D = binr.ReadBytes(elems);
                elems = GetIntegerSize(binr);
                P = binr.ReadBytes(elems);
                elems = GetIntegerSize(binr);
                Q = binr.ReadBytes(elems);
                elems = GetIntegerSize(binr);
                DP = binr.ReadBytes(elems);
                elems = GetIntegerSize(binr);
                DQ = binr.ReadBytes(elems);
                elems = GetIntegerSize(binr);
                IQ = binr.ReadBytes(elems);
                // ------- create RSACryptoServiceProvider instance and initialize with public key -----
                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
                RSAParameters RSAparams = new RSAParameters();
                RSAparams.Modulus = MODULUS;
                RSAparams.Exponent = E;
                RSAparams.D = D;
                RSAparams.P = P;
                RSAparams.Q = Q;
                RSAparams.DP = DP;
                RSAparams.DQ = DQ;
                RSAparams.InverseQ = IQ;
                RSA.ImportParameters(RSAparams);
                return RSA;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message + e.StackTrace);
                return null;
            }
            finally
            {
                binr.Close();
            }
        }

        private static int GetIntegerSize(BinaryReader binr)
        {
            byte bt = 0;
            byte lowbyte = 0x00;
            byte highbyte = 0x00;
            int count = 0;
            bt = binr.ReadByte();
            if (bt != 0x02)        //expect integer
                return 0;
            bt = binr.ReadByte();
            if (bt == 0x81)
                count = binr.ReadByte();    // data size in next byte
            else if (bt == 0x82)
            {
                highbyte = binr.ReadByte();    // data size in next 2 bytes
                lowbyte = binr.ReadByte();
                byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
                count = BitConverter.ToInt32(modint, 0);
            }
            else
            {
                count = bt;        // we already have the data size
            }

            while (binr.ReadByte() == 0x00)
            {    //remove high order zeros in data
                count -= 1;
            }
            binr.BaseStream.Seek(-1, SeekOrigin.Current);        //last ReadByte wasn't a removed zero, so back up a byte
            return count;
        }

    }
}
